/// @tags: TODO BIT ODT
#include <algorithm>
#include <cstdio>
#include <iostream>
#include <set>
#include <vector>
#define lowbit(x) (x & -x)

using namespace std;

namespace BlueQuantum {

int const N = 4e3 + 5;

struct Node {
  int l, r, x;
  Node(int l = 0, int r = 0, int x = 0) : l(l), r(r), x(x) {}
  inline bool operator<(Node const& rhs) const { return l < rhs.l; }
};

struct Query {
  int opt, l, r, x;
} qu[N];

set<Node> odt;

typedef set<Node>::iterator Iter;

int n, q;
int a[N];
vector<int> vec[N], val[N];

inline void add(int c, int p, int v) {
  p = lower_bound(vec[c].begin(), vec[c].end(), p) - vec[c].begin();
  while (p < static_cast<int>(vec[c].size())) {
    val[c][p] += v;
    p += lowbit(p);
  }
}

inline int queryBIT(int c, int p) {
  p = lower_bound(vec[c].begin(), vec[c].end(), p) - vec[c].begin();
  int res = 0;
  while (p) {
    res += val[c][p];
    p -= lowbit(p);
  }
  return res;
}

inline int queryBIT(int c, int l, int r) {
  l = lower_bound(vec[c].begin(), vec[c].end(), l) - vec[c].begin();
  r = lower_bound(vec[c].begin(), vec[c].end(), r) - vec[c].begin();
  return queryBIT(c, r) - queryBIT(c, l - 1);
}

inline Iter split(int p) {
  Iter i = odt.lower_bound(Node(p, 0, 0));
  if (i != odt.end() && i->l == p) return i;
  --i;
  int l = i->l, r = i->r, x = i->x;
  add(x, l, -(r - l + 1));
  odt.erase(i);
  add(x, l, p - l);
  odt.insert(Node(l, p - 1, x));
  add(x, p, r - p + 1);
  return odt.insert(Node(p, r, x)).first;
}

inline void modify(int l, int r, int x) {
  Iter rit = split(r + 1), lit = split(l);
  for (Iter i = lit; i != rit; ++i) add(i->x, i->l, -(i->r - i->l + 1));
  odt.erase(lit, rit);
  add(x, l, r - l + 1);
  odt.insert(Node(l, r, x));
}

inline int query(int l, int r, int x) {
  Iter rit = split(r + 1), lit = split(l);
  return queryBIT(x, l, r);
}

inline int main() {
  cin >> n >> q;
  for (int i = 1; i <= n; ++i) {
    cin >> a[i];
    vec[a[i]].push_back(i);
    odt.insert(Node(i, i, a[i]));
  }
  for (int i = 1; i <= q; ++i) {
    cin >> qu[i].opt >> qu[i].l >> qu[i].r >> qu[i].x;
    if (qu[i].opt == 1) {
      vec[qu[i].x].push_back(qu[i].l);
      vec[qu[i].x].push_back(qu[i].r + 1);
    }
  }
  for (int i = 1; i <= n; ++i) {
    vec[i].push_back(0);
    sort(vec[i].begin(), vec[i].end());
    vector<int>::iterator it = unique(vec[i].begin(), vec[i].end());
    while (vec[i].end() != it) vec[i].pop_back();
    val[i].resize(vec[i].size());
  }
  for (int i = 1; i <= n; ++i) add(a[i], i, 1);
  for (int i = 1; i <= q; ++i) switch (qu[i].opt) {
      case 1:
        modify(qu[i].l, qu[i].r, qu[i].x);
        break;
      case 2:
        cout << query(qu[i].l, qu[i].r, qu[i].x) << '\n';
    }
  return 0;
}

}  // namespace BlueQuantum

int main() {
#ifndef ONLINE_JUDGE
#ifdef LOCAL
  freopen("/tmp/CodeTmp/testdata.in", "r", stdin);
  freopen("/tmp/CodeTmp/testdata.out", "w", stdout);
#else
  freopen("sqrt.in", "R", stdin);
  freopen("sqrt.out", "w", stdout);
#endif
#endif

  ios::sync_with_stdio(false), cin.tie(NULL), cout.tie(NULL);
  return BlueQuantum::main();
}
